package itsm.isperp.workflow.cmd;

import itsm.isperp.framework.core.context.ContextHolder;
import itsm.isperp.framework.utils.HibernateUUIDGenerator;
import itsm.isperp.module.entity.bpm.BpmActionHistory;
import itsm.isperp.module.service.bpm.BpmActionHistoryService;
import itsm.isperp.workflow.graph.ActivitiHistoryGraphBuilder;
import itsm.isperp.workflow.graph.Edge;
import itsm.isperp.workflow.graph.Graph;
import itsm.isperp.workflow.graph.Node;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.activiti.engine.impl.HistoricActivityInstanceQueryImpl;
import org.activiti.engine.impl.Page;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.HistoricActivityInstanceEntity;
import org.activiti.engine.impl.persistence.entity.HistoricTaskInstanceEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;

/**
 * 撤销任务.
 */
public class WithdrawTaskCmd implements Command<Integer> {
	private static Logger logger = LoggerFactory
			.getLogger(WithdrawTaskCmd.class);
	private String historyTaskId;
	private String actionUserId;
	private String processId;

	public WithdrawTaskCmd(String historyTaskId, String processId,
			String actionUserId) {
		this.historyTaskId = historyTaskId;
		this.actionUserId = actionUserId;
		this.processId = processId;
	}

	/**
	 * 撤销流程.
	 * 
	 * @return 0-撤销成功 1-流程结束 2-下一结点已经通过,不能撤销
	 */
	public Integer execute(CommandContext commandContext) {
		HistoricTaskInstanceEntity historicTaskInstanceEntity = Context
				.getCommandContext().getHistoricTaskInstanceEntityManager()
				.findHistoricTaskInstanceById(historyTaskId);
		HistoricActivityInstanceEntity historicActivityInstanceEntity = getHistoricActivityInstanceEntity(historyTaskId);

		Graph graph = new ActivitiHistoryGraphBuilder(
				historicTaskInstanceEntity.getProcessInstanceId()).build();

		Node node = graph.findById(historicActivityInstanceEntity.getId());

		if (!checkCouldWithdraw(node)) {
			logger.info("cannot withdraw {}", historyTaskId);

			return 2;
		}

		// 删除所有活动中的task
		this.deleteActiveTasks(historicTaskInstanceEntity
				.getProcessInstanceId());

		// 获得期望撤销的节点后面的所有节点历史
		List<String> historyNodeIds = new ArrayList<String>();
		collectNodes(node, historyNodeIds);
		this.deleteHistoryActivities(historyNodeIds);
		// 恢复期望撤销的任务和历史
		this.processHistoryTask(historicTaskInstanceEntity,
				historicActivityInstanceEntity);

		logger.info("activiti is withdraw {}",
				historicTaskInstanceEntity.getName());

		// 记录
		BpmActionHistoryService bpmActionHistoryService = ContextHolder
				.getSpringBean(BpmActionHistoryService.class);

		BpmActionHistory entity = new BpmActionHistory();
		entity.setId(HibernateUUIDGenerator.generate());
		entity.setActionName("撤销");
		entity.setActionTime(new Timestamp(new Date().getTime()));
		entity.setActionUserId(this.actionUserId);

		entity.setProcessId(historicTaskInstanceEntity.getProcessInstanceId());
		entity.setTaskId(this.historyTaskId);
		entity.setRelationId(this.processId);

		bpmActionHistoryService.saveOrUpdate(entity);

		return 0;
	}

	public HistoricActivityInstanceEntity getHistoricActivityInstanceEntity(
			String historyTaskId) {
		logger.info("historyTaskId : {}", historyTaskId);

		JdbcTemplate jdbcTemplate = ContextHolder
				.getSpringBean(JdbcTemplate.class);
		String historicActivityInstanceId = jdbcTemplate.queryForObject(
				"select id_ from ACT_HI_ACTINST where task_id_=?",
				String.class, historyTaskId);
		logger.info("historicActivityInstanceId : {}",
				historicActivityInstanceId);

		HistoricActivityInstanceQueryImpl historicActivityInstanceQueryImpl = new HistoricActivityInstanceQueryImpl();
		historicActivityInstanceQueryImpl
				.activityInstanceId(historicActivityInstanceId);

		HistoricActivityInstanceEntity historicActivityInstanceEntity = (HistoricActivityInstanceEntity) Context
				.getCommandContext()
				.getHistoricActivityInstanceEntityManager()
				.findHistoricActivityInstancesByQueryCriteria(
						historicActivityInstanceQueryImpl, new Page(0, 1))
				.get(0);

		return historicActivityInstanceEntity;
	}

	public boolean checkCouldWithdraw(Node node) {
		// TODO: 如果是catchEvent，也应该可以撤销，到时候再说
		for (Edge edge : node.getEdges()) {
			Node dest = edge.getDest();
			String type = dest.getType();

			if ("userTask".equals(type)) {
				if (!dest.isActive()) {
					logger.info("cannot withdraw, " + type + "("
							+ dest.getName() + ") is complete.");

					return false;
				}
			} else if (type.endsWith("Gateway")) {
				if (!checkCouldWithdraw(dest)) {
					return false;
				}
			} else {
				logger.info("cannot withdraw, " + type + "(" + dest.getName()
						+ ") is complete.");

				return false;
			}
		}

		return true;
	}

	public void deleteActiveTasks(String processInstanceId) {
		Context.getCommandContext().getTaskEntityManager()
				.deleteTasksByProcessInstanceId(processInstanceId, null, true);
	}

	public void collectNodes(Node node, List<String> historyNodeIds) {

		for (Edge edge : node.getEdges()) {
			logger.info("edge : {}", edge.getName());

			Node dest = edge.getDest();
			historyNodeIds.add(dest.getId());
			collectNodes(dest, historyNodeIds);
		}
	}

	public void deleteHistoryActivities(List<String> historyNodeIds) {
		JdbcTemplate jdbcTemplate = ContextHolder
				.getSpringBean(JdbcTemplate.class);
		logger.info("historyNodeIds : {}", historyNodeIds);

		for (String id : historyNodeIds) {
			jdbcTemplate.update("delete from ACT_HI_ACTINST where id_=?", id);
		}
	}

	public void processHistoryTask(
			HistoricTaskInstanceEntity historicTaskInstanceEntity,
			HistoricActivityInstanceEntity historicActivityInstanceEntity) {
		historicTaskInstanceEntity.setEndTime(null);
		historicTaskInstanceEntity.setDurationInMillis(null);
		historicActivityInstanceEntity.setEndTime(null);
		historicActivityInstanceEntity.setDurationInMillis(null);

		TaskEntity task = TaskEntity.create(new Date());
		task.setProcessDefinitionId(historicTaskInstanceEntity
				.getProcessDefinitionId());
		task.setId(historicTaskInstanceEntity.getId());
		task.setAssigneeWithoutCascade(historicTaskInstanceEntity.getAssignee());
		task.setParentTaskIdWithoutCascade(historicTaskInstanceEntity
				.getParentTaskId());
		task.setNameWithoutCascade(historicTaskInstanceEntity.getName());
		task.setTaskDefinitionKey(historicTaskInstanceEntity
				.getTaskDefinitionKey());
		task.setExecutionId(historicTaskInstanceEntity.getExecutionId());
		task.setPriority(historicTaskInstanceEntity.getPriority());
		task.setProcessInstanceId(historicTaskInstanceEntity
				.getProcessInstanceId());
		task.setDescriptionWithoutCascade(historicTaskInstanceEntity
				.getDescription());

		Context.getCommandContext().getTaskEntityManager().insert(task);

		ExecutionEntity executionEntity = Context.getCommandContext()
				.getExecutionEntityManager()
				.findExecutionById(historicTaskInstanceEntity.getExecutionId());
		executionEntity
				.setActivity(getActivity(historicActivityInstanceEntity));
	}

	public ActivityImpl getActivity(
			HistoricActivityInstanceEntity historicActivityInstanceEntity) {
		ProcessDefinitionEntity processDefinitionEntity = new FindProcessDefinitionEntityCmd(
				historicActivityInstanceEntity.getProcessDefinitionId())
				.execute(Context.getCommandContext());

		return processDefinitionEntity
				.findActivity(historicActivityInstanceEntity.getActivityId());
	}
}
